﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using ComLib.Lang.Core;
using ComLib.Lang.Docs;
using ComLib.Lang.Helpers;
using ComLib.Lang.Runtime.Switches;

namespace Fluentscript.Exe.Lib.Switches
{
    public class MetadataSwitch : Switch
    {
        private string _scriptsFolder;
        private StringBuilder _buffer;
        private XmlTextWriter _writer;
        private Dictionary<string, CustomTag> _map;

        /// <summary>
        /// Initialize with the metadata switch.
        /// </summary>
        /// <param name="scriptsFolder"></param>
        public MetadataSwitch(string scriptsFolder)
        {
            _scriptsFolder = scriptsFolder;
        }


        /// <summary>
        /// Generates the xml metadata for each script.
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public override object DoExecute(ComLib.Lang.Interpreter i)
        {
            if (!Directory.Exists(_scriptsFolder))
                throw new DirectoryNotFoundException("Directory for plugins : " + _scriptsFolder + " does not exist");

            var files = Directory.GetFiles(_scriptsFolder);
            this.InitWriter();
            _buffer.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            
            _writer.WriteStartElement("scripts");
            foreach (var file in files)
            {
                var content = File.ReadAllText(file);
                var fileInfo = new FileInfo(file);
                var metadata = Extract(content);
                if (!string.IsNullOrEmpty(metadata))
                {
                    var tags = Parse(metadata);

                    _map = new Dictionary<string, CustomTag>();
                    foreach (var customTag in tags.CustomTags)
                        _map[customTag.Name] = customTag;

                    if(_map.ContainsKey("name"))
                    {
                       _map["name"].Content = fileInfo.Name;
                    }
                    ToXml(_map, tags);
                }
            }
            _writer.WriteEndElement();
            _writer.Flush();
            var xml = _buffer.ToString();
            File.WriteAllText("metadata.xml", xml);
            return null;
        }


        private void InitWriter()
        {
            _buffer = new StringBuilder();
            var sw = new StringWriter(_buffer);
            var xtw = new XmlTextWriter(sw);
            xtw.Formatting = Formatting.Indented;
            _writer = xtw;
        }


        /// <summary>
        /// Extract the metadata section from the content
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private static string Extract(string content)
        {
            if (string.IsNullOrEmpty(content))
                return string.Empty;

            var ndxStart = content.IndexOf("@scriptmeta-start");
            var ndxEnd = content.IndexOf("@scriptmeta-end");

            if (ndxStart < 0)
                return string.Empty;

            var length = ndxEnd - (ndxStart + 17);
            var metadata = content.Substring(ndxStart + 17, length);
            return metadata;
        }


        private static DocTags Parse(string content)
        {
            var reader = new StringReader(content);
            var currentLine = reader.ReadLine();
            var lines = new List<string>();

            // More to read.
            while (currentLine != null)
            {
                // new Token(TokenKind.Comment, TokenTypes.CommentSLine, currentLine, currentLine)
                lines.Add(currentLine);
                currentLine = reader.ReadLine();
            }
            var result = DocHelper.ParseDocTags(lines);
            return result.Item1;
        }


        private void ToXml(IDictionary<string, CustomTag> map, DocTags tags)
        {
            _writer.WriteStartElement("script");
            WriteElementStringFromMap("name", string.Empty);
            WriteElementString("summary", tags.Summary);
            WriteElementStringFromMap("scripttype"    , string.Empty);
            WriteElementStringFromMap("company"       , "CodeHelix Solutions Inc");
            WriteElementStringFromMap("author"        , "CodeHelix Solutions Inc");
            WriteElementStringFromMap("doc"  		   , string.Empty);
            WriteElementStringFromMap("license"  	   , string.Empty);
            WriteElementStringFromMap("script-version",  string.Empty);
            WriteElementStringFromMap("tags"  		   , string.Empty);
            WriteElementStringFromMap("isInstalled"   ,  string.Empty);
            _writer.WriteStartElement("args");
            
            foreach(var arg in tags.Args)
            {
                _writer.WriteStartElement("arg");
                var example = (arg.Examples != null && arg.Examples.Count > 0) ? arg.Examples[0] : string.Empty;
                
                _writer.WriteAttributeString("name",     arg.Name);
                _writer.WriteAttributeString("type",     arg.Type);
                _writer.WriteAttributeString("required", arg.Required.ToString());
                _writer.WriteAttributeString("examples", example);
                _writer.WriteEndElement();
            }
            _writer.WriteEndElement();
            _writer.WriteEndElement();
        }


        private void WriteElementString(string key, string value)
        {
            _writer.WriteElementString(key, value);
        }


        private void WriteElementStringFromMap(string key, string defaultValue)
        {
            _writer.WriteElementString(key, GetMapValueOrDefault(key, defaultValue));
        }


        private string GetMapValueOrDefault(string key, string defaultValue)
        {
            var val = _map.ContainsKey(key) ? _map[key].Content : defaultValue;
            return val;
        }


        private void Wrap(string key, StringBuilder builder, IDictionary<string, CustomTag> map, DocTags tags, string tabs)
        {
            var val = map.ContainsKey(key) ? map[key].Content : string.Empty;
            var content = tabs + "<" + key + ">" + val + "</" + key + ">" + Environment.NewLine;
            builder.Append(content);
        }
    }
}
